 @include "lib.awk"
 @include "readcsv.awk"
 BEGIN {  Discretizer="nbins" }
 function readbins(n,f,name,lo,hi,goals,d,w,has,at,\
                       name0,lo0,hi0,goals0,d0,w0,has0,\
                       src,breaks) {
   src= "cat " data(f ".csv")                               # get the source
   readcsv(name0,lo0,hi0,goals0,d0,w0,has0,src)             # read the raw data
   @Discretizer(n,name0,lo0,hi0,goals0,d0,w0,has0, breaks)  # find numeric breaks
   bins(breaks, name0,lo0,hi0,goals0,d0,w0,has0,            # do the discretizing
                name ,lo ,hi ,goals ,d ,w ,has, at)
 }
 function bins(breaks,name0,lo0,hi0,goals0,d0,w0,has0,   \
               name ,lo ,hi ,goals ,d ,w ,has,at,   \
               c,r,class,val) {
   for(c in lo0) # for numeric goals, copy the min,max ranges
     if  (c in goals0) { lo[c] = lo0[c]; hi[c] = hi0[c] }
   copy(w0,w) # feature weights and goals get copied verbatim
   copy(goals0,goals)
   for(c in name0) # names loss '$' signs (since none are now is numeric)        
       name[c] = (c in goals0) ? name0[c] : gensub(/\$/,"","g",name0[c])
   for(r in d0) {
     class = @FindClass(goals0,r,d0) 
     for(c in d0[r]) {
       val = d0[r][c]  # by default, the new value is the same as old
       if (val !~ /\?/) { 
         if (! (c in  goals0) && c in breaks) # but some vals get discretized
           val = bin(d0[r][c],breaks[c])
         has[c][val][class]++     # remember what classes have what symbols
         at[c][val][r]            # remember what values are in which rows
       }
       d[r][c]=val                # create new data
     }}}
 function bin(val,breaks,   n,i) {# return the first break that contains 'val'
   n = length(breaks)
   for(i=1;i<n;i++)
     if (val >= breaks[i] && val < breaks[i+1])
       return i
   return n  - 1  # if in doubt, return max bin
 }
